﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Silverlight3dApp.Learning.Lesson09
{
    public class ParticleExplosion
    {
        private static readonly Random s_Random = new Random();

        private VertexPositionTexture[] _vertices;
        private Vector3[] _directions;
        private Color[] _colors;
        private VertexBuffer _vertexBuffer;

        private readonly GraphicsDevice _graphicsDevice;
        private readonly SilverlightEffect _effect;

        private readonly ParticleSettings _particleSettings;

        private Vector3 _position;

        private int _lifeLeft;

        private int _numberParticlesPerRound;
        private int _maxParticles;
        private int _roundTime;
        private int _timeSinceLastRound;

        private int _endOfLiveParticlesIndex;
        private int _endOfDeadParticlesIndex;

        public bool IsDead
        {
            get
            {
                return _endOfDeadParticlesIndex == _maxParticles;
            }
        }

        public ParticleExplosion(GraphicsDevice graphicsDevice, Vector3 position, int lifeLeft, int roundTime, int numberParticlesPerRound, int maxParticles, ParticleSettings particleSettings, SilverlightEffect effect)
        {
            _graphicsDevice = graphicsDevice;
            _position = position;
            _lifeLeft = lifeLeft;
            _roundTime = roundTime;
            _numberParticlesPerRound = numberParticlesPerRound;
            _maxParticles = maxParticles;
            _particleSettings = particleSettings;
            _effect = effect;

            InitializeVertices();
        }

        private void InitializeVertices()
        {
            _vertices = new VertexPositionTexture[_maxParticles * 4];
            _directions = new Vector3[_maxParticles];
            _colors = new Color[_maxParticles];

            for (var index = 0; index < _maxParticles; index++)
            {
                var size = (float) (s_Random.NextDouble() * _particleSettings.MaxSize);
                _vertices[index * 4] = new VertexPositionTexture(_position, Vector2.Zero);
                _vertices[(index * 4) + 1] = new VertexPositionTexture(new Vector3(_position.X + size, _position.Y, _position.Z), Vector2.UnitX);
                _vertices[(index * 4) + 2] = new VertexPositionTexture(new Vector3(_position.X, _position.Y + size, _position.Z), Vector2.UnitY);
                _vertices[(index * 4) + 3] = new VertexPositionTexture(new Vector3(_position.X + size, _position.Y + size, _position.Z), Vector2.One);

                var direction = new Vector3((float) (s_Random.NextDouble() * 2 - 1), (float) (s_Random.NextDouble() * 2 - 1), (float) (s_Random.NextDouble() * 2 - 1));
                direction.Normalize();
                direction *= (float) s_Random.NextDouble();
                _directions[index] = direction;

                //_colors[index] = new Color((int) (s_Random.NextDouble() * 256), (int) (s_Random.NextDouble() * 256), (int) (s_Random.NextDouble() * 256));
                _colors[index] = Colors.Yellow;
            }

            _vertexBuffer = new VertexBuffer(_graphicsDevice, VertexPositionTexture.VertexDeclaration, _vertices.Length, BufferUsage.None);
        }

        public void Update(GameTime gameTime)
        {
            if (_lifeLeft > 0)
            {
                _lifeLeft -= gameTime.ElapsedGameTime.Milliseconds;
            }
            _timeSinceLastRound += gameTime.ElapsedGameTime.Milliseconds;
            if (_timeSinceLastRound > _roundTime)
            {
                _timeSinceLastRound -= _roundTime;
                if (_endOfLiveParticlesIndex < _maxParticles)
                {
                    _endOfLiveParticlesIndex += _numberParticlesPerRound;
                    if (_endOfLiveParticlesIndex > _maxParticles)
                    {
                        _endOfLiveParticlesIndex = _maxParticles;
                    }
                }
                if (_lifeLeft <= 0)
                {
                    if (_endOfDeadParticlesIndex < _maxParticles)
                    {
                        _endOfDeadParticlesIndex += _numberParticlesPerRound;
                        if (_endOfDeadParticlesIndex > _maxParticles)
                        {
                            _endOfDeadParticlesIndex = _maxParticles;
                        }
                    }
                }
            }

            for (var index = _endOfDeadParticlesIndex; index < _endOfLiveParticlesIndex; index++)
            {
                _vertices[index * 4].Position += _directions[index];
                _vertices[(index * 4) + 1].Position += _directions[index];
                _vertices[(index * 4) + 2].Position += _directions[index];
                _vertices[(index * 4) + 3].Position += _directions[index];
            }
        }

        public void Draw(Camera camera)
        {
            _graphicsDevice.SetVertexBuffer(_vertexBuffer);
            if (_endOfDeadParticlesIndex < _endOfLiveParticlesIndex)
            {
                for (var index = _endOfDeadParticlesIndex; index < _endOfLiveParticlesIndex; index++)
                {
                    _effect.Parameters["WorldViewProjection"].SetValue(camera.View * camera.Projection);
                    _effect.Parameters["ParticleColor"].SetValue(_colors[index]);

                    foreach (var pass in _effect.CurrentTechnique.Passes)
                    {
                        pass.Apply();
                        _graphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, index * 4, 2);
                    }
                }
            }
        }
    }
}